package cn.mrcode.wxsdk.web.service;

import cn.mrcode.wxsdk.core.dialogue.common.RandomStringGenerator;
import cn.mrcode.wxsdk.core.dialogue.common.XMLParaser;
import cn.mrcode.wxsdk.core.dialogue.common.exception.ReqException;
import cn.mrcode.wxsdk.core.dialogue.common.exception.WxException;
import cn.mrcode.wxsdk.core.dialogue.common.util.DateUtil;
import cn.mrcode.wxsdk.core.dialogue.service.BaseService;
import cn.mrcode.wxsdk.web.common.PaySignature;
import cn.mrcode.wxsdk.web.hander.PayInterfaceReqDataCheckHander;
import cn.mrcode.wxsdk.web.protocol.PayReqData;
import cn.mrcode.wxsdk.web.protocol.req.*;
import cn.mrcode.wxsdk.web.protocol.res.*;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付的公共服务
 * @author zhuqiang
 * @version V1.0
 * @date 2015/8/24 11:59
 */
public class PayCommonService extends BaseService {
    private static Logger log = LoggerFactory.getLogger(PayCommonService.class);
    /** 统一下单接口 **/
    private static final String UNIFIEDORDER_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /** 订单查询接口 **/
    private static final String ORDERQUERY_API = "https://api.mch.weixin.qq.com/pay/orderquery";
    /** 关闭订单接口 **/
    private static final String CLOSEORDER_API = "https://api.mch.weixin.qq.com/pay/closeorder";
    /** 退款接口 **/
    private static final String REFUND_API = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    /** 退款查询接口 **/
    private static final String REFUNDQUERY_API = "https://api.mch.weixin.qq.com/pay/refundquery";

    /**
     * 统一下单
     *
     * @param unifiedOrderReqData
     */
    public static UnifiedOrderResData unifiedorder(UnifiedOrderReqData unifiedOrderReqData, String key) throws ReqException, WxException {
        PayInterfaceReqDataCheckHander.hander(unifiedOrderReqData);
        Map map = handerReq(unifiedOrderReqData, key, UNIFIEDORDER_API,null);
        UnifiedOrderResData result = new UnifiedOrderResData();
        try {
            BeanUtils.populate(result, map);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("bean populate fail",e);
        }
        return result;
    }

    /**
     * 统一下单数据回调处理
     * @param request
     * @return
     */
    public static Map<String,Object> unifiedorderCallBackHanderToMap(HttpServletRequest request){
        try {
            request.setCharacterEncoding("UTF-8");
            ServletInputStream in = request.getInputStream();
            // 本次请求 xml数据
            String reqMsgXml = PaySignature.inputStream2String(in);
            log.debug("PayCallBackResData : " + reqMsgXml);
            Map<String,Object> resParams = XMLParaser.getMapFromXML(reqMsgXml);
//            Map<String,Object> resParams = XMLParaser.parseMap(reqMsgXml);
            return resParams;
        }catch (Exception e) {
            log.error("unifiedorderCallBackHanderToMap fail:",e);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 统一下单处理 返回签名校验 和  返回 对象
     * @param resMap
     * @param key 商户密钥 该key，可以根据resMap中使用  appid属性获取到appid，然后根据appid找到对应的key
     * @return
     * @throws ReqException 返回通信异常，return_code 如果有值，就返回对应的 错误code 和 信息，属于微信异常
     */
    public static PayCallBackResData unifiedorderCallBackHander(Map<String,Object> resMap, String key) throws WxException {

            boolean b = PaySignature.checkIsSignValidFrom(resMap, key);
            if(!b){
                throw new RuntimeException("API返回的数据签名验证不通过，有可能被第三方篡改!!!");
            }
            String  return_code = (String) resMap.get("return_code");
            if("FAIL".equals(return_code)) {
                String errMsg = (String) resMap.get("return_msg");
                if(errMsg != null){
                    throw new WxException(return_code , errMsg);
                }
            }
        try {
            PayCallBackResData result = new PayCallBackResData();
            BeanUtils.populate(result, resMap);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 统一处理，请求数据。包括打印日志，和处理 协议返回错误码和错误信息
     *
     * @param reqData 请求数据
     * @param key     商户kei
     * @param api     api地址
     * @param appid 是否使用证书请求,有值使用
     * @return 成功返回处理好的回包数据map，因为支付里面有类似list的元素，所以，需要调用处自己处理
     *
     * @throws ReqException
     */
    private static Map handerReq(PayReqData reqData, String key, String api, String appid) throws WxException, ReqException {
        String xml = null;
        try {
            xml = XMLParaser.toXml(reqData);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
        String resultXml = postXml(api, xml,appid);
        Map map = handerPayThrowErrcode(resultXml, key);
        return map;
    }




    /**
     * 订单查询，代金券的一律都暂未处理
     *
     * @param orderQueryReqData
     *
     * @return
     */
    public static OrderQueryResData orderQuery(OrderQueryReqData orderQueryReqData, String key) throws ReqException, WxException {
        PayInterfaceReqDataCheckHander.hander(orderQueryReqData);
        Map map = handerReq(orderQueryReqData, key, ORDERQUERY_API,null);
        OrderQueryResData result = new OrderQueryResData();
        try {
            BeanUtils.populate(result, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 关闭订单api
     *
     * @param closeOrderReqData
     * @param key
     *
     * @return
     *
     * @throws ReqException
     */
    public static CloseOrderResData closeorder(CloseOrderReqData closeOrderReqData, String key) throws ReqException, WxException {
        PayInterfaceReqDataCheckHander.hander(closeOrderReqData);
        Map map = handerReq(closeOrderReqData, key, CLOSEORDER_API,null);
        CloseOrderResData result = new CloseOrderResData();
        try {
            BeanUtils.populate(result, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 申请订单退款,需要使用证书
     *
     * @param refundReqData
     * @param key
     *
     * @return
     */
    public static RefundResData refund(RefundReqData refundReqData, String key) throws ReqException, WxException {
        PayInterfaceReqDataCheckHander.hander(refundReqData);
        Map map = handerReq(refundReqData, key, REFUND_API, refundReqData.getAppid());
        RefundResData result = new RefundResData();
        try {
            BeanUtils.populate(result, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 退款查询
     *
     * @param refundReqData
     * @param key
     *
     * @return
     *
     * @throws ReqException
     */
    public static RefundQueryResData refundQuery(RefundQueryReqData refundReqData, String key) throws ReqException, WxException {
        PayInterfaceReqDataCheckHander.hander(refundReqData);
        Map map = handerReq(refundReqData, key, REFUNDQUERY_API,null);
        RefundQueryResData result = new RefundQueryResData();
        try {
            BeanUtils.populate(result, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取 app 端 支付参数
     * @param unifiedorder 统一下单数据回包
     * @param key 商户密钥
     * @return
     */
    public static Map<String,String> getAppPayData(UnifiedOrderResData unifiedorder, String key){
        /**
         * 参与签名的字段名为appid，partnerid，prepayid，noncestr，timestamp，package。注意：package的值格式为Sign=WXPay
         */
        //封装 jssdk 需要的参数信息
        Map<String,String> map = new HashMap<String, String>();
        map.put("appid",unifiedorder.getAppid());
        map.put("partnerid",unifiedorder.getMch_id());
        map.put("prepayid",unifiedorder.getPrepay_id());
        map.put("package","Sign=WXPay");
        map.put("noncestr", RandomStringGenerator.getRandomStringByLength(32));
        map.put("timestamp", DateUtil.getNowTimestamp());
        String sign = PaySignature.getMapSignStr(map, key);
        map.put("sign", sign);  //签名

        return map;
    }
}
